#include "Precompile.h"
#include "WindowsEngineInterface.h"
#include "WinMessagesInputSystem.h"
#include "DirectInputSystem.h"

namespace Core{
	WindowsEngineInterface::WindowsEngineInterface(){
	
	}

	void WindowsEngineInterface::initialize(HINSTANCE instance, int cmdShow, EnginePlatforms platform, Graphics::RenderMode& mode){
		m_renderThreadRunning = false;
		m_platform = platform;
		m_mode = mode;
		m_hinstance = instance;
		m_nCmdShow = cmdShow;
		m_messageEvent = boost::shared_ptr<Core::Event<MSG*>>(new Core::Event<MSG*>);

		if(FAILED(initWindow(m_hinstance,m_nCmdShow,mode))){
			LOG_ERROR("Unable to create window");
			assert(0);
			return;
		}

		// Create threads
		m_renderThread = ThreadLib::ThreadFactory::spawnThread(boost::function<void(void)>(boost::bind(&WindowsEngineInterface::renderThread,this)));
		m_soundThread = ThreadLib::ThreadFactory::spawnThread(boost::function<void(void)>(boost::bind(&WindowsEngineInterface::soundThread,this)));

		// Start threads
		m_renderThread->run();
		m_soundThread->run();

		// Creation of input system
		switch(m_platform){
			case EP_DX10:
			case EP_DX9:
				m_inputSystem = Input::InputFactory::createInputSystem(Input::IST_DINPUT);
				if(m_inputSystem){
					boost::shared_ptr<Input::DirectInputInitParams> params = boost::shared_ptr<Input::DirectInputInitParams>(new Input::DirectInputInitParams);
					params->m_hinst = m_hinstance;
					params->m_hwnd = m_hwnd;

					m_inputSystem->initialize(params);
				}
				else{
					// Error handling
					LOG_ERROR("EngineInterface::inputThread - Failed to initialize input system.");
					assert(0);
					return;
				}
				break;
		}

		while(!getRenderSystemPtr() || !getRenderSystemPtr()->readyForUse()){}
	}

	void WindowsEngineInterface::run(){
		/*RECT ScreenRect;
		GetWindowRect(m_hwnd,&ScreenRect);
		ClipCursor(&ScreenRect);*/

		// Main loop
		MSG msg = {0};
		while(GetMessage( &msg, NULL, 0, 0 ))
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
			m_messageEvent->invoke( &msg );

			if(getInputSystemPtr()->keyIsPressed(Input::EIK_ESCAPE))
				break;

			if(msg.message == WM_DESTROY || msg.message == WM_CLOSE)
				break;

			switch(msg.message){
			case WM_WINDOWPOSCHANGED:
			case WM_SETFOCUS:
			case WM_EXITSIZEMOVE:
				/*GetWindowRect(m_hwnd,&ScreenRect);
				ClipCursor(&ScreenRect);*/
				break;
			}
		}
	}

	void WindowsEngineInterface::renderThread(){
		// Creation of rendersystem
		switch(m_platform){
			case EP_DX10:
				m_renderSystem = Graphics::GraphicsFactory::createRenderSystem(m_mode,Graphics::RSP_DX10);
				if(m_renderSystem){
					m_renderSystem->initialize(m_hwnd);
				}
				else{
					// Error handling
					LOG_ERROR("EngineInterface::renderThread - Failed to initialize rendersystem.");
					assert(0);
					return;
				}
				break;
			case EP_DX9:
				m_renderSystem = Graphics::GraphicsFactory::createRenderSystem(m_mode,Graphics::RSP_DX9);
				if(m_renderSystem){
					m_renderSystem->initialize(m_hwnd);
				}
				else{
					// Error handling
					LOG_ERROR("EngineInterface::renderThread - Failed to initialize rendersystem.");
					assert(0);
					return;
				}
				break;
		}
	
		// Main rendering loop
		m_renderThreadRunning = true;
		while(m_renderThreadRunning){
			m_renderSystem->update();

			/*boost::system_time time = boost::get_system_time(); 
			time += boost::posix_time::millisec(0); 
			boost::thread::sleep(time);*/
		}
	}

	void WindowsEngineInterface::soundThread(){
		m_soundSystem = Sound::SoundFactory::createSoundSystem();
		if(!m_soundSystem){
			LOG_ERROR("Failed to create a sound system.");
			assert(0);
			return;
		}

		m_soundThreadRunning = true;
		while(m_soundThreadRunning){
			if(!m_soundSystem->update()){
				LOG_ERROR("Failed in updating sound system.");
				assert(0);
			}

			boost::system_time time = boost::get_system_time();
			time += boost::posix_time::millisec(1);
			boost::thread::sleep(time);
		}
	}

	LRESULT CALLBACK WindowsEngineInterface::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
		PAINTSTRUCT ps;
		HDC hdc;

		switch (message) 
		{
			case WM_PAINT:
				hdc = BeginPaint(hWnd, &ps);
				EndPaint(hWnd, &ps);
				break;

			case WM_DESTROY:
				PostQuitMessage(0);
				break;

			default:
				return DefWindowProc(hWnd, message, wParam, lParam);
		}

		return 0;
	}

	HRESULT WindowsEngineInterface::initWindow(HINSTANCE hInstance, int nCmdShow, Graphics::RenderMode &mode){
		// Register class
		WNDCLASSEX wcex;
		wcex.cbSize			= sizeof(WNDCLASSEX); 
		wcex.style          = CS_HREDRAW | CS_VREDRAW;
		wcex.lpfnWndProc    = &WindowsEngineInterface::WndProc;
		wcex.cbClsExtra     = 0;
		wcex.cbWndExtra     = 0;
		wcex.hInstance      = hInstance;
		wcex.hIcon          = LoadIcon(hInstance, (LPCTSTR)107);
		wcex.hCursor        = NULL;
		wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
		wcex.lpszMenuName   = NULL;
		wcex.lpszClassName  = L"Engine";
		wcex.hIconSm        = LoadIcon(wcex.hInstance, (LPCTSTR)107);
		if(!RegisterClassEx(&wcex)){
			LOG_ERROR("initWindow - Failed to register windows class.");
			assert(0);
		}

		// Create window
		m_hinstance = hInstance;
		std::pair<int,int> res = mode.resolutionXY;
		RECT rc = { 0, 0, res.first, res.second };
		AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
		m_hwnd = CreateWindow( L"Engine", L"Direct3D", WS_OVERLAPPEDWINDOW,
							   CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
		if(!m_hwnd){
			LOG_ERROR("initWindow - Failed to create window.");
			assert(0);
		}

		ShowWindow( m_hwnd, m_nCmdShow );
		ShowCursor( FALSE );
   
		return S_OK;
	}
}